/*=========================================================================
  UberSim Source Code Release
  -------------------------------------------------------------------------
  Copyright (C) 2002 Manuela Veloso, Brett Browning, Mike Bowling,
  James Bruce; {mmv, brettb, mhb, jbruce}@cs.cmu.edu
  Erick Tryzelaar {erickt}@andrew.cmu.edu
  School of Computer Science, Carnegie Mellon University
  -------------------------------------------------------------------------
  This software is distributed under the GNU General Public License,
  version 2.  If you do not have a copy of this licence, visit
  www.gnu.org, or write: Free Software Foundation, 59 Temple Place,
  Suite 330 Boston, MA 02111-1307 USA.  This program is distributed
  in the hope that it will be useful, but WITHOUT ANY WARRANTY,
  including MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  -------------------------------------------------------------------------*/

#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>

#include "mmgr.h"

#include "CollisionManager.h"
#include "Engine.h"
#include "Node.h"
#include "Simulator.h"
#include "Timer.h"

/*******************************/

Engine* Engine::getInstance ()
{
  static Engine instance;

  return &instance;
}

/*******************************/

Engine::~Engine ()
{
  shutdown ();
}

/*******************************/

void Engine::initialize ()
{
  if (!isInitialized) {
    // units: dm, kg, s
    isInitialized = true;

    root = new Node ("root");

    Simulator::getInstance        ()->initialize ();
    CollisionManager::getInstance ()->initialize ();

    isPaused        = true;
    isTakingOneStep = false;
    isSkippingFrame = false;

    frameCount   = 0;
    startTime    = 0.0;
    currentTime  = 0.0;
    previousTime = 0.0;
    deltaTime    = 0.0;
    fps          = 0.0;

    isShutdown    = false;
  }
}

/*******************************/

void Engine::shutdown ()
{
  if (!isShutdown) {
    isShutdown = true;

    delete root;
    root = 0;

    Simulator::getInstance        ()->shutdown ();
    CollisionManager::getInstance ()->shutdown ();

    isInitialized = false;
  }
}

/*******************************/

Node* Engine::getRoot ()
{
  return root;
}

/*******************************/

bool Engine::getIsPaused ()
{
  return isPaused;
}

/*******************************/

bool Engine::getIsTakingOneStep ()
{
  return isTakingOneStep;
}

/*******************************/

bool Engine::getIsSkippingFrame ()
{
  return isSkippingFrame;
}

/*******************************/

Real Engine::getStartTime ()
{
  return startTime;
}

/*******************************/

Real Engine::getCurrentTime ()
{
  return currentTime;
}

/*******************************/

Real Engine::getDeltaTime ()
{
  return deltaTime;
}

/*******************************/

unsigned int Engine::getFrameCount ()
{
  return frameCount;
}

/*******************************/

Real Engine::getFps ()
{
  return fps;
}

/*******************************/

Real Engine::getTargetFps ()
{
  return targetFps;
}

/*******************************/

void Engine::setIsPaused (bool isPaused)
{
  this->isPaused = isPaused;
}

/*******************************/

void Engine::setIsTakingOneStep (bool isTakingOneStep, int nSteps)
{
  this->isTakingOneStep = isTakingOneStep;
  this->stepsToTake = nSteps;
}

/*******************************/

void Engine::setTargetFps (Real targetFps)
{
  this->targetFps = targetFps;

  Simulator::getInstance ()->setTargetFps (targetFps);
}

/*******************************/

void Engine::beginFrame ()
{
#if 0
  static timeval time;
  gettimeofday (&time, 0);

  if (currentTime == 0.0) {
    startTime    = (Real) time.tv_sec + ((Real) time.tv_usec / 1000000.0);
    currentTime  = startTime;
    previousTime = currentTime;
    deltaTime = currentTime - previousTime;
  }  else    {
    Real ct   = (Real) time.tv_sec + ((Real) time.tv_usec / 1000000.0);
    deltaTime = ct - previousTime;

    if (deltaTime < 1.0 / targetFps)        {
      isSkippingFrame = true;
    }      else        {
      isSkippingFrame = false;
		
      previousTime = currentTime;
      currentTime  = ct;
      //deltaTime    = targetFps * frameCount;
    }
  }
#endif

  if (!isSkippingFrame && (!isPaused || isTakingOneStep))    {
    frameCount += 1;
    //    fps         = 1.0 / (Real) deltaTime;
    fps         = 1.0 / targetFps;

    Timer::getInstance            ()->beginFrame ();
    CollisionManager::getInstance ()->beginFrame ();
    Simulator::getInstance        ()->beginFrame ();
  }
}

/*******************************/

void Engine::frame ()
{
  if (!isSkippingFrame && (!isPaused || isTakingOneStep)) {
    Timer::getInstance            ()->frame ();
    CollisionManager::getInstance ()->frame ();
    Simulator::getInstance        ()->frame ();
		
    root->updateGeometricState (0.0);//deltaTime);

    if (isTakingOneStep && --stepsToTake <= 0)
      isTakingOneStep = false;

  }

  root->draw ();
}

/*******************************/

void Engine::endFrame ()
{
  if (!isSkippingFrame && (!isPaused || isTakingOneStep))    {
    Timer::getInstance            ()->endFrame ();
    CollisionManager::getInstance ()->endFrame ();
    Simulator::getInstance        ()->endFrame ();
  }

  //  usleep (0);
}

/*******************************/

Engine::Engine ()
{
  isInitialized = false;
  isShutdown    = true;
}

/*******************************/
